x11: emit ::enter/leave-monitor
authorOlivier Fourdan <ofourdan@redhat.com>
Thu, 16 Apr 2020 10:58:39 +0000 (12:58 +0200)
committerOlivier Fourdan <ofourdan@redhat.com>
Thu, 16 Apr 2020 14:23:57 +0000 (16:23 +0200)
For the X11 backend, keep a list of monitors for which the surface
intersects the monitor area.

Whenever the X11 surface is configured, check against the list of
monitors to determine whether it enters a new monitor or if it left a
monitor, to emit the corresponding ::enter/leave-monitor signals just
like a Wayland compositor would.

As monitors can be added, removed or reconfigured at any time, redo
those checks whenever any of these events occur.

gdk/x11/gdkdisplay-x11.c
gdk/x11/gdkprivate-x11.h
gdk/x11/gdksurface-x11.c
gdk/x11/gdksurface-x11.h

index 28578693433a9da8dfecd5ee33190f6c42cca7c9..7da3255687dec3d7dc338d0178f1846bc77c5eb4 100644 (file)
@@ -990,6 +990,7 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
                }
 
               gdk_x11_surface_update_popups (surface);
+              gdk_x11_surface_enter_leave_monitors (surface);
            }
         }
       break;
index 70c12159d594fb06e2a86494786806f3c3f14250..667b14a1421ee304667bc8cca5fec1a42b3c8273 100644 (file)
@@ -217,6 +217,8 @@ void _gdk_x11_surface_register_dnd (GdkSurface *window);
 
 void gdk_x11_surface_update_popups (GdkSurface *surface);
 
+void gdk_x11_surface_enter_leave_monitors (GdkSurface *surface);
+
 GdkDrag        * _gdk_x11_surface_drag_begin (GdkSurface          *window,
                                               GdkDevice          *device,
                                               GdkContentProvider *content,
index 3607629819c48e609dd17f2a56183ddef3a2b133..01708cbc85fa5a37fb2a28062cd6dcd456ee8303 100644 (file)
@@ -110,6 +110,10 @@ static void     set_wm_name                       (GdkDisplay  *display,
                                                   const gchar *name);
 static void     move_to_current_desktop           (GdkSurface *surface);
 static void     gdk_x11_toplevel_state_callback   (GdkSurface *surface);
+static void     gdk_x11_surface_on_monitor_added   (GdkSurface *surface,
+                                                    GdkMonitor *monitor);
+static void     gdk_x11_surface_on_monitor_removed (GdkSurface *surface,
+                                                    GdkMonitor *monitor);
 
 /* Return whether time1 is considered later than time2 as far as xserver
  * time is concerned.  Accounts for wraparound.
@@ -134,6 +138,7 @@ gdk_x11_surface_init (GdkX11Surface *impl)
 {  
   impl->surface_scale = 1;
   impl->frame_sync_enabled = TRUE;
+  impl->surface_is_on_monitor = NULL;
 }
 
 GdkToplevelX11 *
@@ -462,8 +467,17 @@ gdk_x11_surface_finalize (GObject *object)
       _gdk_x11_display_remove_window (display, impl->xid);
       if (impl->toplevel && impl->toplevel->focus_window)
         _gdk_x11_display_remove_window (display, impl->toplevel->focus_window);
+
+      g_signal_handlers_disconnect_by_func (display,
+                                            gdk_x11_surface_on_monitor_added,
+                                            GDK_SURFACE (object));
+      g_signal_handlers_disconnect_by_func (display,
+                                            gdk_x11_surface_on_monitor_removed,
+                                            GDK_SURFACE (object));
     }
 
+  g_clear_pointer (&impl->surface_is_on_monitor, g_list_free);
+
   g_free (impl->toplevel);
 
   if (impl->cursor)
@@ -974,6 +988,13 @@ _gdk_x11_display_create_surface (GdkDisplay     *display,
 
   gdk_surface_freeze_updates (surface);
 
+  g_signal_connect_swapped (surface->display, "monitor-added",
+                            G_CALLBACK (gdk_x11_surface_on_monitor_added),
+                            surface);
+  g_signal_connect_swapped (surface->display, "monitor-removed",
+                            G_CALLBACK (gdk_x11_surface_on_monitor_removed),
+                            surface);
+
   return surface;
 }
 
@@ -1557,6 +1578,86 @@ gdk_x11_surface_update_popups (GdkSurface *parent)
     }
 }
 
+static void
+gdk_x11_surface_set_is_on_monitor (GdkSurface *surface,
+                                   GdkMonitor *monitor,
+                                   gboolean    is_on_monitor)
+{
+  GdkX11Surface *impl = GDK_X11_SURFACE (surface);
+  GList *was_on_monitor;
+
+  was_on_monitor = g_list_find (impl->surface_is_on_monitor, monitor);
+
+  if (!was_on_monitor && is_on_monitor)
+    {
+      impl->surface_is_on_monitor = g_list_append (impl->surface_is_on_monitor,
+                                                   monitor);
+      gdk_surface_enter_monitor (surface, monitor);
+    }
+  else if (was_on_monitor && !is_on_monitor)
+    {
+      impl->surface_is_on_monitor = g_list_remove (impl->surface_is_on_monitor,
+                                                   monitor);
+      gdk_surface_leave_monitor (surface, monitor);
+    }
+}
+
+static void
+gdk_x11_surface_check_monitor (GdkSurface *surface,
+                               GdkMonitor *monitor)
+{
+  GdkRectangle monitor_geometry;
+  GdkRectangle surface_geometry;
+  gboolean is_on_monitor;
+
+  gdk_monitor_get_geometry (monitor, &monitor_geometry);
+  gdk_surface_get_geometry (surface,
+                            &surface_geometry.x,
+                            &surface_geometry.y,
+                            &surface_geometry.width,
+                            &surface_geometry.height);
+
+  is_on_monitor = gdk_rectangle_intersect (&surface_geometry,
+                                           &monitor_geometry,
+                                           NULL);
+
+  gdk_x11_surface_set_is_on_monitor (surface, monitor, is_on_monitor);
+}
+
+void
+gdk_x11_surface_enter_leave_monitors (GdkSurface *surface)
+{
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  int n_monitors, i;
+
+  n_monitors = gdk_display_get_n_monitors (display);
+  for (i = 0; i < n_monitors; i++)
+    {
+      GdkMonitor *monitor = gdk_display_get_monitor (display, i);
+      gdk_x11_surface_check_monitor (surface, monitor);
+    }
+}
+
+static void
+gdk_x11_surface_on_monitor_added (GdkSurface *surface,
+                                  GdkMonitor *monitor)
+{
+  gdk_x11_surface_check_monitor (surface, monitor);
+  g_signal_connect_swapped (G_OBJECT (monitor), "notify::geometry",
+                            G_CALLBACK (gdk_x11_surface_check_monitor),
+                            surface);
+}
+
+static void
+gdk_x11_surface_on_monitor_removed (GdkSurface *surface,
+                                    GdkMonitor *monitor)
+{
+  gdk_x11_surface_check_monitor (surface, monitor);
+  g_signal_handlers_disconnect_by_func (G_OBJECT (monitor),
+                                        gdk_x11_surface_check_monitor,
+                                        monitor);
+}
+
 static void gdk_x11_surface_set_geometry_hints (GdkSurface         *surface,
                                                 const GdkGeometry *geometry,
                                                 GdkSurfaceHints     geom_mask);
index 609181f59ac5cde5110329f12d267a3c1442bd15..53abd500e5f4fd9c9a80773f31d9655a0e0838bb 100644 (file)
@@ -80,6 +80,8 @@ struct _GdkX11Surface
   int abs_y;
 
   guint64 map_time;
+
+  GList *surface_is_on_monitor;
 };
  
 struct _GdkX11SurfaceClass